#!/usr/bin/python

import pprint
import math

from sys import argv

# Some globals
diceSides = 6
debug = False

pp = pprint.PrettyPrinter(indent=4)

def getProbDicts(params):

    # Obtain a dict of all possible outcomes
    atkOutcomes = getRawOutcomes(params['atkDice'])
    defOutcomes = getRawOutcomes(params['defDice'])

    # Determine the probabilities of specific MoS values
    successDict = getSuccessDistribution(params, atkOutcomes, defOutcomes)

    # Evaluate the damage result of the MoS values
    damageDict = evaluateResults(params, successDict)


def evaluateResults(params, successDict):
    dam = params['dam']
    armor = params['armor']
    boxes = params['boxes']

    print "Evaluated %iD%i%+i vs. %iD%i%+i for damage %i vs. armor %i (%i boxes)" % (
        params['atkDice'], diceSides, params['atkMod'],
        params['defDice'], diceSides, params['defMod'],
        params['dam'], params['armor'], params['boxes']
        ),
    if params['isSniper']:
        print " [SNIPER]",
    if params['isMB']:
        print " [MR]",
    if params['mb'] > 0:
        print " [MB:%i]" % (params["mb"]),
    print ""

    labels = ["O", "SO", "EX"]
    for label in labels:
        rngDict = successDict[label]
        if debug:
            print "Got (%s) dict = {%s}" % (label, rngDict)

        if params['mb'] > 0 and label is not "O":
            dam = dam - params['mb']
            if debug:
                print "Reduced damage to: %i" % dam

        results = {}
        for x in rngDict.iterkeys():
            # x is a MoS
            prob = rngDict[x]
            damage = dam * float(x)
            res = math.floor(damage / armor)
            if debug:
                print "Res %f occurs from floor(%f x %i) = %f (%2.3f)" % (res, dam, x, res, prob)

            if res > boxes: 
                if debug:
                    print "Appending OK results (%6.3f) to bucket %i" % (prob, boxes)
                res = boxes

            if res in results:
                results[res] = results[res] + prob
            else:
                results[res] = prob
    
            if x not in results and x <= boxes:
                results[x] = 0.0

        print "{%2s} results:" % (label),
        for y in results:
            print " %i box (%6.3f%%)" % (y, results[y]),
        print ""
    

def getSuccessDistribution(params, atkResults, defResults):
   
    successDict = {} 
    for rangeMod in range(0, -3, -1):
        if params['isSniper']:
            atkMod = params['atkMod'] - 1
        else:
            atkMod = params['atkMod'] + rangeMod

        defMod = params['defMod']

        rngDict = getRangeResults(atkResults, atkMod, defResults, defMod)

        if rangeMod == 0:
            successDict["O"] = rngDict
        elif rangeMod == -1:
            successDict["SO"] = rngDict
        elif rangeMod == -2:
            successDict["EX"] = rngDict
       
    return successDict

def getRangeResults(atkResults, atkMod, defResults, defMod):
    results = {}
    count = 0
    for atkDiceRes in atkResults:
        atkRes = 0
        if atkDiceRes != 0:
            atkRes = atkDiceRes + atkMod

        for defDiceRes in defResults:
            defRes = 0
            if defDiceRes != 0:
                defRes = defDiceRes + defMod
            
            result = 0
            if atkRes > defRes:
                result = atkRes - defRes

            count = count + 1
            if result in results:
                results[result] = results[result] + 1
            else:
                results[result] = 1

            if debug:
                print "%iD%i%+i = %i vs. %iD%i%+i = %i -> %i" % (
                    params['atkDice'], diceSides, atkMod, atkDiceRes,
                    params['defDice'], diceSides, defMod, defDiceRes,
                    result)
    
    checkCount = 0    
    for x in results.iterkeys():
        y = results[x]
        prob = (float(results[x]) / float(count)) * 100.0
        if debug:
            print "  MoS %i has %i occurances (%2.3f)" % (x, y, prob)
        results[x] = prob

        checkCount = checkCount + y
    if debug:
        totalCount = len(atkResults) * len(defResults)
        print "Predicted %i | Observed: %i" % (totalCount, checkCount)

    return results

def getRawOutcomes(numDice):
    outcomes = []
    # Generate the raw list that contains all possible outcomes
    for x in range(1, numDice+1):
        outcomes = appendDiceSides(outcomes, diceSides)
    
    # For each element, reduce it to a pre-modifier value
    results = []
    for x in outcomes:
        res = 0
        for y in x:
            if y == 6 and res >= 6:
                res = res + 1
            elif y > 1 and y > res:
                res = y
        results.append(res)
        if debug:
            print "Raw Outcome of %s has result %i " % (x, res)

    return results

def appendDiceSides(diceList, diceSides):

    if len(diceList) is 0:
        for x in range(1, diceSides+1):
            diceList.append([x])
        return diceList

    newList = []
    for y in diceList:
        # y is an element of list type
        for x in range(1, diceSides+1):
            z = y[:]
            z.append(x)
            newList.append(z)

    return newList

if __name__ == '__main__':
    params = {
        'atkDice': 2,
        'atkMod': 0,
        'defDice': 2,
        'defMod': 0,
        'dam' : 8,
        'armor': 15,
        'boxes': 3,
        'isSniper': False,
        'isMB': False,
        'mb': 0
    }

    if len(argv) >= 2:
        params['atkDice'] = int(argv[1])
    if len(argv) >= 3:
        params['atkMod'] = int(argv[2])
    if len(argv) >= 4:
        params['defDice'] = int(argv[3])
    if len(argv) >= 5:
        params['defMod'] = int(argv[4])
    if len(argv) >= 6:
        params['dam'] = float(argv[5])
    if len(argv) >= 7:
        params['armor'] = float(argv[6])
    if len(argv) >= 8:
        params['boxes'] = float(argv[7])
    if len(argv) >= 9:
        params['isSniper'] = int(argv[8])
    if len(argv) >= 10:
        params['isMR'] = int(argv[9])
    if len(argv) >= 11:
        params['mb'] = int(argv[10])

    probDict = getProbDicts(params)

